﻿using CPPEI.ETL.DataServiceInterface;
using Microsoft.CSharp;
using System;
using System.CodeDom;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net;
using System.Reflection;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using System.Web.Services.Description;
using System.Web.Services.Discovery;
using System.Xml.Schema;

namespace CPPEI.ETL.DSImplement
{
    public static class WSTool
    {
        /// <summary>                       
        /// 根据web   service文档架构向代理类添加ServiceDescription和XmlSchema                             
        /// </summary>                                  
        /// <param   name="baseWSDLUrl">web服务地址</param>                           
        /// <param   name="importer">代理类</param>                              
        private static void checkForImports(DataServiceInfo info, string baseWsdlUrl, ServiceDescriptionImporter importer)
        {
            DiscoveryClientProtocol dcp = new DiscoveryClientProtocol();

            if (info.NeedAuth)
            {
                // 设置认证信息
                dcp.Credentials = new NetworkCredential(info.AuthUser, info.AuthPassword);
            }

            dcp.DiscoverAny(baseWsdlUrl);
            dcp.ResolveAll();
            foreach (object osd in dcp.Documents.Values)
            {
                if (osd is ServiceDescription)
                {
                    importer.AddServiceDescription((ServiceDescription)osd, null, null);
                }
                if (osd is XmlSchema)
                {
                    importer.Schemas.Add((XmlSchema)osd);
                }
            }
        }

        /// <summary>
        /// 创建并加载
        /// </summary>
        /// <param name="info"></param>
        /// <returns></returns>
        public static Type CreateAndLoadWSClientType(DataServiceInfo info)
        {
            string url = info.Address;
            string assemblyFileName = GetWsAssemblyFile(url);

            FileInfo fileInfo = new FileInfo(assemblyFileName);
            if (!fileInfo.Directory.Exists)
            {
                fileInfo.Directory.Create();
                fileInfo.Directory.Attributes = FileAttributes.Normal;
            }
            else
            {
                fileInfo.Directory.Attributes = FileAttributes.Normal;
            }

            bool isDeleted = true;
            if (File.Exists(assemblyFileName))
            {
                try
                {
                    // 删除已存在的文件
                    File.Delete(assemblyFileName);
                    isDeleted = true;
                }
                catch (Exception ex)
                {
                    Logger.WarningToTag(Library.LogTAGWS, string.Format("删除文件{0}出错，不重新生成代理类，直接加载文件, ex:{1}", assemblyFileName, ex));
                    isDeleted = false;
                }
            }

            if (isDeleted)
            {
                string @namespace = "EnterpriseServerBase.WebService.DynamicWebCalling";

                //获取WSDL
                WebClient webClient = new WebClient();
                if (info.NeedAuth)
                {
                    // 设置认证信息
                    webClient.Credentials = new NetworkCredential(info.AuthUser, info.AuthPassword);
                }
                string wsdlUrl = url;
                if (wsdlUrl.ToUpper().EndsWith("?WSDL") == false)
                {
                    wsdlUrl = string.Format("{0}?wsdl", wsdlUrl);
                }
                Stream stream = webClient.OpenRead(wsdlUrl);// + "?WSDL");
                ServiceDescription sd = ServiceDescription.Read(stream);
                ServiceDescriptionImporter sdi = new ServiceDescriptionImporter();

                //生成属性和生成异步方法
                //importer.CodeGenerationOptions = CodeGenerationOptions.GenerateProperties | CodeGenerationOptions.GenerateNewAsync;

                sdi.ProtocolName = "Soap"; // 指定访问协议。  
                sdi.Style = ServiceDescriptionImportStyle.Client; // 生成客户端代理。 
                sdi.AddServiceDescription(sd, "", "");
                CodeNamespace cn = new CodeNamespace(@namespace);

                //生成客户端代理类代码
                CodeCompileUnit ccu = new CodeCompileUnit();
                ccu.Namespaces.Add(cn);

                checkForImports(info, url, sdi);

                sdi.Import(cn, ccu);
                CSharpCodeProvider csc = new CSharpCodeProvider();

                //设定编译参数
                CompilerParameters cplist = new CompilerParameters();
                cplist.GenerateExecutable = false;
                cplist.OutputAssembly = assemblyFileName;
                cplist.ReferencedAssemblies.Add("System.dll");
                cplist.ReferencedAssemblies.Add("System.XML.dll");
                cplist.ReferencedAssemblies.Add("System.Web.Services.dll");
                cplist.ReferencedAssemblies.Add("System.Data.dll");

                //编译代理类
                CompilerResults cr = csc.CompileAssemblyFromDom(cplist, ccu);
                if (true == cr.Errors.HasErrors)
                {
                    System.Text.StringBuilder sb = new System.Text.StringBuilder();
                    foreach (System.CodeDom.Compiler.CompilerError ce in cr.Errors)
                    {
                        sb.Append(ce.ToString());
                        sb.Append(System.Environment.NewLine);
                    }
                    Logger.ErrorToTag(Library.LogTAGWS, string.Format("编译代理类出错:{0}", sb.ToString()));
                    throw new Exception(sb.ToString());
                }

                //生成代理实例，并调用方法
                System.Reflection.Assembly assembly = cr.CompiledAssembly;
                return GetWsClassType(assembly);
            }
            else
            {
                System.Reflection.Assembly assembly = Assembly.LoadFrom(assemblyFileName);
                return GetWsClassType(assembly);
            }
        }

        /// <summary>
        /// 读取webservice获得信息
        /// </summary>
        /// <param name="url"></param>
        public static Type LoadWSClientType(DataServiceInfo info)
        {
            string url = info.Address;
            string assemblyFileName = GetWsAssemblyFile(url);

            FileInfo fileInfo = new FileInfo(assemblyFileName);
            if(!fileInfo.Directory.Exists)
            {
                fileInfo.Directory.Create();
            }

            if (File.Exists(assemblyFileName))
            {
                //生成代理实例，并调用方法
                System.Reflection.Assembly assembly = Assembly.Load(File.ReadAllBytes(assemblyFileName));
                return GetWsClassType(assembly);
            }
            else
            {
                string @namespace = "EnterpriseServerBase.WebService.DynamicWebCalling";
                try
                {
                    //获取WSDL
                    WebClient webClient = new WebClient();
                    if(info.NeedAuth)
                    {
                        // 设置认证信息
                        webClient.Credentials = new NetworkCredential(info.AuthUser, info.AuthPassword);
                    }
                    Stream stream = webClient.OpenRead(url);// + "?WSDL");
                    ServiceDescription sd = ServiceDescription.Read(stream);
                    ServiceDescriptionImporter sdi = new ServiceDescriptionImporter();
                    sdi.ProtocolName = "Soap"; // 指定访问协议。  
                    sdi.Style = ServiceDescriptionImportStyle.Client; // 生成客户端代理。 
                    sdi.AddServiceDescription(sd, "", "");
                    CodeNamespace cn = new CodeNamespace(@namespace);

                    //生成客户端代理类代码
                    CodeCompileUnit ccu = new CodeCompileUnit();
                    ccu.Namespaces.Add(cn);

                    sdi.Import(cn, ccu);
                    CSharpCodeProvider csc = new CSharpCodeProvider();

                    //设定编译参数
                    CompilerParameters cplist = new CompilerParameters();
                    cplist.GenerateExecutable = false;
                    cplist.OutputAssembly = assemblyFileName;
                    cplist.ReferencedAssemblies.Add("System.dll");
                    cplist.ReferencedAssemblies.Add("System.XML.dll");
                    cplist.ReferencedAssemblies.Add("System.Web.Services.dll");
                    cplist.ReferencedAssemblies.Add("System.Data.dll");

                    //编译代理类
                    CompilerResults cr = csc.CompileAssemblyFromDom(cplist, ccu);
                    if (true == cr.Errors.HasErrors)
                    {
                        System.Text.StringBuilder sb = new System.Text.StringBuilder();
                        foreach (System.CodeDom.Compiler.CompilerError ce in cr.Errors)
                        {
                            sb.Append(ce.ToString());
                            sb.Append(System.Environment.NewLine);
                        }
                        throw new Exception(sb.ToString());
                    }

                    //生成代理实例，并调用方法
                    System.Reflection.Assembly assembly = cr.CompiledAssembly;
                    return GetWsClassType(assembly);
                }
                catch (Exception ex)
                {
                    throw new Exception(ex.InnerException.Message, new Exception(ex.InnerException.StackTrace));
                }
            }
        }

        /// <summary>
        /// 获取soap的header信息，用于做认证
        /// </summary>
        /// <param name="info"></param>
        /// <returns></returns>
        public static Type LoadSoapHeaderType(DataServiceInfo info)
        {
            string url = info.Address;
            string assemblyFileName = GetWsAssemblyFile(url);

            FileInfo fileInfo = new FileInfo(assemblyFileName);
            if (!fileInfo.Directory.Exists)
            {
                fileInfo.Directory.Create();
            }

            if (File.Exists(assemblyFileName))
            {
                ////生成代理实例，并调用方法
                //System.Reflection.Assembly assembly = Assembly.LoadFrom(assemblyFileName);
                //Type[] types = assembly.GetTypes();
                //for (int index = 0; index < types.Length; index++)
                //{
                //    if(types[index].IsSubclassOf(System.Web.Services.Protocols.SoapHeader))
                //    {
                //        return types[index];
                //    }
                //}
            }
            return null;
        }

        #region InvokeWebService
        //动态调用web服务
        public static object InvokeWebService(DataSourceInfo info, Dictionary<string, object> args)
        {
            string assemblyFileName = GetWsAssemblyFile(info.Address);

            FileInfo fileInfo = new FileInfo(assemblyFileName);
            if(!fileInfo.Directory.Exists)
            {
                fileInfo.Directory.Create();
            }

            if (File.Exists(assemblyFileName))
            {
                //生成代理实例，并调用方法
                System.Reflection.Assembly assembly = Assembly.LoadFrom(assemblyFileName);
                Type t = GetWsClassType(assembly);
                object obj = assembly.CreateInstance(t.FullName);// Activator.CreateInstance(t);

                if (info.NeedAuth)
                {
                    // 设置认证信息
                    SetAuthorization(obj, info.AuthType, info.AuthUser, info.AuthPassword);
                }

                System.Reflection.MethodInfo mi = t.GetMethod(info.DataSource);

                return InvokeMethod(obj, mi, args);
            }
            else
            {
                string @namespace = "EnterpriseServerBase.WebService.DynamicWebCalling";

                //获取WSDL
                WebClient wc = new WebClient();
                string wsdlUrl = info.Address;
                if (wsdlUrl.ToUpper().EndsWith("?WSDL") == false)
                {
                    wsdlUrl = string.Format("{0}?wsdl", wsdlUrl);
                }
                Stream stream = wc.OpenRead(wsdlUrl);// + "?WSDL");
                ServiceDescription sd = ServiceDescription.Read(stream);
                ServiceDescriptionImporter sdi = new ServiceDescriptionImporter();
                sdi.ProtocolName = "Soap"; // 指定访问协议。  
                sdi.Style = ServiceDescriptionImportStyle.Client; // 生成客户端代理。 
                sdi.AddServiceDescription(sd, "", "");
                CodeNamespace cn = new CodeNamespace(@namespace);

                //生成客户端代理类代码
                CodeCompileUnit ccu = new CodeCompileUnit();
                ccu.Namespaces.Add(cn);
                sdi.Import(cn, ccu);
                CSharpCodeProvider csc = new CSharpCodeProvider();

                //设定编译参数
                CompilerParameters cplist = new CompilerParameters();
                cplist.GenerateExecutable = false;
                cplist.OutputAssembly = assemblyFileName;
                cplist.ReferencedAssemblies.Add("System.dll");
                cplist.ReferencedAssemblies.Add("System.XML.dll");
                cplist.ReferencedAssemblies.Add("System.Web.Services.dll");
                cplist.ReferencedAssemblies.Add("System.Data.dll");

                //编译代理类
                CompilerResults cr = csc.CompileAssemblyFromDom(cplist, ccu);
                if (true == cr.Errors.HasErrors)
                {
                    System.Text.StringBuilder sb = new System.Text.StringBuilder();
                    foreach (System.CodeDom.Compiler.CompilerError ce in cr.Errors)
                    {
                        sb.Append(ce.ToString());
                        sb.Append(System.Environment.NewLine);
                    }
                    throw new Exception(sb.ToString());
                }

                //生成代理实例，并调用方法
                System.Reflection.Assembly assembly = Assembly.LoadFrom(assemblyFileName);
                Type t = GetWsClassType(assembly);
                object obj = Activator.CreateInstance(t);

                if (info.NeedAuth)
                {
                    // 设置认证信息
                    SetAuthorization(obj, info.AuthType, info.AuthUser, info.AuthPassword);
                }

                System.Reflection.MethodInfo mi = t.GetMethod(info.DataSource);

                return InvokeMethod(obj, mi, args);
            }
        }

        private static void SetAuthorization(object client, int type, string username, string password)
        {
            System.Web.Services.Protocols.WebClientProtocol webClient = client as System.Web.Services.Protocols.WebClientProtocol;
            if (webClient == null)
            {
                return;
            }

            //if(true || type == DEF.WSAuthenticationType.Windows)
            //{
            //webClient.Credentials = new System.Net.NetworkCredential("OSBINTERFACE", "osb_interface"); // new NetworkCredential(username, password);
            webClient.Credentials = new NetworkCredential(username, password);
            //}
        }

        /// <summary>
        /// 调用方法
        /// </summary>
        /// <param name="instance"></param>
        /// <param name="method"></param>
        /// <param name="argDict">path,value</param>
        /// <returns></returns>
        private static object InvokeMethod(object instance, MethodInfo method, Dictionary<string, object> argDict)
        {
            ParameterInfo[] parameters = method.GetParameters();
            
            object[] actualArgs =null;
            if(parameters.Length > 0)
            {
                actualArgs = new object[parameters.Length];
                for(int index = 0; index <parameters.Length; index++)
                {
                    var parameter = parameters[index];
                    if (!parameter.ParameterType.IsClass || parameter.ParameterType == typeof(string))
                    {
                        object bingoValue = null;
                        foreach (var key in argDict.Keys)
	                    {
		                     if(string.Equals(key, parameter.Name))
                             {
                                 bingoValue = argDict[key];
                                 break;
                             }
                        }
                        actualArgs[index] = bingoValue;
                    }
                    else
                    {
                        object objArg = parameter.ParameterType.Assembly.CreateInstance(parameter.ParameterType.FullName);
                        PropertyInjection.Injection(parameter.ParameterType, objArg, parameter.Name, argDict);
                        actualArgs[index] = objArg;
                    }
                }
            }
            return method.Invoke(instance, actualArgs); ;
        }

        private static string GetWsClassName(string wsUrl)
        {
            string[] parts = wsUrl.Split('/');
            string[] pps = parts[parts.Length - 1].Split('.');

            return pps[0];
        }

        /// <summary>
        /// 将webservice地址转换为程序集文件地址
        /// </summary>
        /// <param name="url"></param>
        /// <returns></returns>
        private static string GetWsAssemblyFile(string url)
        {
            string assembyFolder = System.Configuration.ConfigurationManager.AppSettings["AssemblyFolder"];
            string fileName = string.Format("WS{0}", DES.Hash_MD5_32(url, true));
            return string.Format("{0}{1}.dll", assembyFolder, fileName);
        }

        private static Type GetWsClassType(Assembly assembly)
        {
            Type[] types = assembly.GetTypes();
            foreach (var type in types)
            {
                if (type.BaseType == typeof(System.Web.Services.Protocols.SoapHttpClientProtocol))
                {
                    return type;
                }
            }
            return null;
        }
        #endregion
    }

    class DES
    {
        public static string Hash_MD5_32(string word, bool toUpper = true)
        {
            try
            {
                System.Security.Cryptography.MD5CryptoServiceProvider MD5CSP
                    = new System.Security.Cryptography.MD5CryptoServiceProvider();

                byte[] bytValue = System.Text.Encoding.UTF8.GetBytes(word);
                byte[] bytHash = MD5CSP.ComputeHash(bytValue);
                MD5CSP.Clear();

                //根据计算得到的Hash码翻译为MD5码
                string sHash = "", sTemp = "";
                for (int counter = 0; counter < bytHash.Count(); counter++)
                {
                    long i = bytHash[counter] / 16;
                    if (i > 9)
                    {
                        sTemp = ((char)(i - 10 + 0x41)).ToString();
                    }
                    else
                    {
                        sTemp = ((char)(i + 0x30)).ToString();
                    }
                    i = bytHash[counter] % 16;
                    if (i > 9)
                    {
                        sTemp += ((char)(i - 10 + 0x41)).ToString();
                    }
                    else
                    {
                        sTemp += ((char)(i + 0x30)).ToString();
                    }
                    sHash += sTemp;
                }

                //根据大小写规则决定返回的字符串
                return toUpper ? sHash : sHash.ToLower();
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message);
            }
        }

        // 创建Key
        public string GenerateKey()
        {
            DESCryptoServiceProvider desCrypto = (DESCryptoServiceProvider)DESCryptoServiceProvider.Create();
            return ASCIIEncoding.ASCII.GetString(desCrypto.Key);
        }
        // 加密字符串
        public static string EncryptString(string sInputString, string sKey)
        {
            byte[] data = Encoding.UTF8.GetBytes(sInputString);
            DESCryptoServiceProvider DES = new DESCryptoServiceProvider();
            DES.Key = ASCIIEncoding.ASCII.GetBytes(sKey);
            DES.IV = ASCIIEncoding.ASCII.GetBytes(sKey);
            ICryptoTransform desencrypt = DES.CreateEncryptor();
            byte[] result = desencrypt.TransformFinalBlock(data, 0, data.Length);
            //string s1 = BitConverter.ToString(result);

            //string s3 = s1.Replace("-", string.Empty);
            string s2 = toBitString(result);

            return s2;
        }

        // 解密字符串
        public static string DecryptString(string sInputString, string sKey)
        {
            //string[] sInput = sInputString.Split('-');
            //byte[] data = new byte[sInput.Length];
            //for (int i = 0; i < sInput.Length; i++)
            //{
            //    data[i] = byte.Parse(sInput[i], NumberStyles.HexNumber);
            //}

            byte[] data = new byte[sInputString.Length / 2];
            for (int i = 0; i < data.Length; i++)
            {
                data[i] = byte.Parse(sInputString.Substring(i * 2, 2), NumberStyles.HexNumber);
            }
            DESCryptoServiceProvider DES = new DESCryptoServiceProvider();
            DES.Key = ASCIIEncoding.ASCII.GetBytes(sKey);
            DES.IV = ASCIIEncoding.ASCII.GetBytes(sKey);
            ICryptoTransform desencrypt = DES.CreateDecryptor();
            byte[] result = desencrypt.TransformFinalBlock(data, 0, data.Length);
            return Encoding.UTF8.GetString(result);
        }

        internal static string toBitString(byte[] data)
        {
            StringBuilder builder = new StringBuilder();
            for (int index = 0; index < data.Length; index++)
            {
                builder.AppendFormat("{0:X2}", data[index]);
            }
            return builder.ToString();
        }
    }
}
